package com.hongpu.utils;


import com.alibaba.fastjson2.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * jwt token 加密和解密
 *
 * @author 张玉胜
 */
@Slf4j
public class JwtUtils {

    /**
     * 定义 Token 过期时间（例如 8 小时后）,单位：毫秒
     */
    private static final String PUBLIC_KEY = getPublicKey();
    private static final String PRIVATE_KEY = getPrivateKey();
    private static final String ALG = "RS256";




    /**
     * 生成 JWT Token
     *
     * @param userId  用户 ID
     * @param account 用户账号
     * @return 生成的 JWT Token 字符串
     */
    public synchronized static String generateToken(Long userId, String account) {
        try {
            Map<String, Object> headerClaims = new HashMap<>();
            headerClaims.put("alg", ALG);
            headerClaims.put("typ", "JWT");
            JWTCreator.Builder builder = JWT.create();
            builder.withHeader(headerClaims);
            builder.withIssuer(account);
            builder.withSubject(String.valueOf(userId));
            // 定义 Token 过期时间（例如 8 小时后）,单位：毫秒
            long EXPIRATION_MS = System.currentTimeMillis() + 3600 * 1000 * 8 * 10;
            builder.withExpiresAt(new Date(EXPIRATION_MS));
            Date now = new Date(System.currentTimeMillis());
            builder.withIssuedAt(now);
            builder.withJWTId(UUID.randomUUID().toString());
            RSAPublicKey rsaPublicKey = loadRSAPublicKey();
            RSAPrivateKey rsaPrivateKey = loadRSAPrivateKey();
            Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, rsaPrivateKey);
            String token = builder.sign(algorithm);
            return token;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("生成 JWT Token 失败", e.getLocalizedMessage());
            return null;
        }
    }

    public synchronized static DecodedJWT verify(final String token) {
        if (null == token || token.isEmpty()) {
            throw new IllegalArgumentException("令牌不能为空或空字符串");
        }

        RSAPublicKey rsaPublicKey = loadRSAPublicKey();
        Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, null);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
      //  log.info("jwtVerifier: {}", jwtVerifier);
        try {
           // log.info("开始验证令牌: {}", token);
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
           // log.info("令牌验证成功: {}", JSONObject.from(decodedJWT).toString());
            return decodedJWT;
        } catch (JWTVerificationException e) {
          //  log.info("令牌: {}", token, " 验证失败");
         //   log.error("令牌验证失败: {}", e.getMessage());
            throw new JWTVerificationException("JWT 验证失败: " + e.getMessage(), e);
        }
    }


    private static RSAPublicKey loadRSAPublicKey() {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] publicKeyBytes = Base64.getDecoder().decode(PUBLIC_KEY);
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
            return (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("RSA 算法不可用", e);
        } catch (InvalidKeySpecException e) {
            throw new IllegalArgumentException("无效的公钥格式", e);
        }
    }


    private static RSAPrivateKey loadRSAPrivateKey() {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] privateKeyBytes = Base64.getDecoder().decode(PRIVATE_KEY);
            if (null == privateKeyBytes || 0 == privateKeyBytes.length) {
                throw new IllegalArgumentException("私钥为空或空字符串");
            }
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
            return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("不支持RSA算法", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效私钥规范", e);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("私钥数据无效", e);
        }
    }

    private static String getPublicKey() {
        try {
            InputStream inputStream = JwtUtils.class.getClassLoader().getResourceAsStream("public.key");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while (null != (line = bufferedReader.readLine())) {
                stringBuilder.append(line);
            }
          //  log.info("加载公钥成功:{}", stringBuilder.toString());
            return stringBuilder.toString();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static String getPrivateKey() {
        try {
            InputStream inputStream = JwtUtils.class.getClassLoader().getResourceAsStream("private.key");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while (null != (line = bufferedReader.readLine())) {
                stringBuilder.append(line);
            }
          //  log.info("加载私钥成功:{}", stringBuilder.toString());
            return stringBuilder.toString();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}